﻿using Core.SimpleTemp.Application.MenuApp;
using Core.SimpleTemp.Application.ServiceApp.SysApp.LoginLogApp;
using Core.SimpleTemp.Application.UserApp;
using Core.SimpleTemp.Common;
using Core.SimpleTemp.Common.Authorization;
using Core.SimpleTemp.Entitys;
using Core.SimpleTemp.Repositories.Internal;
using Core.SimpleTemp.Repositories.RepositoryEntityFrameworkCore.Sys;
using Core.SimpleTemp.Repositories.Sys;
using Core.Toolkit.Encrypt;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;

namespace Core.SimpleTemp.Application
{
    /// <summary>
    /// 系统登录服务
    /// </summary>
    [AutoDi()]
    public class SysLoginService : IGetFunctionCode
    {
        SysUserRepository _sysUserRepository;
        SysMenuAppService _sysMenuAppService;
        IDistributedCache _distributedCache;
        SysLoginLogAppService _sysLoginLogAppService;
        readonly WebAppOptions _webAppOptions;
        IHttpContextAccessor _httpContextAccessor;
        SysFactoryRepository _sysFactoryRepository;

        public SysLoginService(SysUserRepository sysUserRepository, SysMenuAppService sysMenuAppService, IDistributedCache distributedCache, SysLoginLogAppService sysLoginLogAppService, IOptionsMonitor<WebAppOptions> webAppOptions, IHttpContextAccessor httpContextAccessor, SysFactoryRepository sysFactoryRepository)
        {
            _sysUserRepository = sysUserRepository;
            _sysMenuAppService = sysMenuAppService;
            _distributedCache = distributedCache;
            _sysLoginLogAppService = sysLoginLogAppService;
            _webAppOptions = webAppOptions.CurrentValue;
            _httpContextAccessor = httpContextAccessor;
            _sysFactoryRepository = sysFactoryRepository;
        }


        /// <summary>
        /// 登录
        /// </summary>
        /// <param name="context"></param>
        /// <param name="sysUser"></param>
        /// <returns></returns>
        public async Task<bool> LoginAsync(HttpContext context, SysUser sysUser)
        {
            var md5Password = MD5Helper.MD5(sysUser.Password);
            var user = await _sysUserRepository.FindUserForLoginAsync(sysUser.LoginName, md5Password);
            if (user == null)
            {
                return false;
            }
            //登录日志
            await _sysLoginLogAppService.InsertLogAsync(new SysLoginLogDto { LoginName = user.LoginName, LoginType = LoginType.Cookie });
            //颁发用户票据
            var claimIdentity = await this.CreateClaimsIdentityAsync(user);
            var claimsPrincipal = new ClaimsPrincipal(claimIdentity);
            await context.SignInAsync(claimsPrincipal, new AuthenticationProperties()
            {
                IsPersistent = true// Fasle CookieOption.Expires不生效
                //IssuedUtc 颁发时间无需设置默认 CookieScheme的SingInAsync取当前时间，
                //ExpiresUtc 无需设置默认CookieScheme的SingInAsync 当前时间加option.ExpireTimeSpan
                //同时设定CookieOption.Expires为当前 票据过期时间
                //至此建议票据颁发时期均默认使用 option.ExpireTimeSpan设置
            });
            return true;
        }

        /// <summary>
        /// 登录
        /// </summary>
        /// <param name="context"></param>
        /// <param name="sysUser"></param>
        /// <returns></returns>
        public async Task<string> JwtAuthenticate(string userName, string pwd)
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.ASCII.GetBytes(_webAppOptions.JwtIssuerSigningKey);//Jwt秘钥
            var authTime = DateTime.UtcNow;
            var expiresAt = authTime.AddMinutes(_webAppOptions.TimeOutOfLogin);//过期时间

            //验证账号&密码信息
            var md5Password = MD5Helper.MD5(pwd);
            var user = await _sysUserRepository.FindUserForLoginAsync(userName, md5Password);
            if (user == null)
            {
                return "用户密码信息验证失败";
            }
            //登录日志
            await _sysLoginLogAppService.InsertLogAsync(new SysLoginLogDto { LoginName = user.LoginName, LoginType = LoginType.Cookie });
            #region 构建票据基础信息
            //创建用户claimIdentity
            var claimIdentity = await this.CreateClaimsIdentityAsync(user);
            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = claimIdentity,
                Issuer = _webAppOptions.JwtValidIssuer,
                Audience = _webAppOptions.JwtValidAudience,
                Expires = expiresAt,

                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
            };
            #endregion
            //生成票据
            var token = tokenHandler.CreateToken(tokenDescriptor);
            var tokenString = tokenHandler.WriteToken(token);
            return tokenString;
        }

        /// <summary>
        /// 退出登录
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>

        public async Task SignOutAsync(HttpContext context)
        {
            var sidClaim = context.User.FindFirst(ClaimTypes.Sid);
            //退出
            await context.SignOutAsync();
            try
            {
                //删除权限缓存
                await _distributedCache.RemoveAsync(SysConsts.MENU_CACHEKEY_PREFIX + sidClaim?.Value);
            }
            catch (System.Exception)
            {
            }
        }

        /// <summary>
        /// 创建用户ClaimsIdentity
        /// </summary>
        /// <returns></returns>
        private async Task<ClaimsIdentity> CreateClaimsIdentityAsync(SysUser user)
        {
            var claimIdentity = new ClaimsIdentity("Cookie");
            claimIdentity.AddClaim(new Claim(ClaimTypes.Sid, user.Id.ToString()));//用户ID
            claimIdentity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.LoginName));//登录名
            claimIdentity.AddClaim(new Claim(ClaimTypes.Name, user.Name));//用户名

            #region 业务逻辑
            var factories = await _sysFactoryRepository.GetFactoriesAsync();
            var headOffice = factories.Where(a => a.FactoryType == "1").FirstOrDefault();//总公司标识
            var isHeadOffice = user.SysUserInfor.WorkCompany == headOffice.Id;
            claimIdentity.AddClaim(new Claim("headoffice", isHeadOffice.ToString()));//用户名
            //工厂
            if (user?.SysUserInfor?.WorkCompany != null)
            {
                claimIdentity.AddClaim(new Claim("factory", user.SysUserInfor.WorkCompany?.ToString()));
            }
            #endregion


            //var roleIds = await _sysUserRepository.FindUserRoleAsync(user.Id);
            //foreach (var roleId in roleIds)
            //{
            //    claimIdentity.AddClaim(new Claim(ClaimTypes.Role, roleId.ToString()));
            //}
            return claimIdentity;
        }

        /// <summary>
        /// 根据用户ID以及用户名
        /// </summary>
        /// <param name="sysUserDto"></param>
        /// <returns></returns>
        public Task<List<SysMenuDto>> GetMenusAndFunctionByUserAsync(SysUserDto sysUserDto)
        {
            return _sysMenuAppService.GetMenusAndFunctionByUserAsync(sysUserDto);
        }

        /// <summary>
        /// 获取当前票据用户菜单和方法信息
        /// </summary>
        /// <param name="sysUserDto"></param>
        /// <returns></returns>
        public async Task<List<SysMenuDto>> GetMenusAndFunctionByCurrentUserAsync()
        {
            var sidClaim = _httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.Sid);
            var nameIdentifierClaim = _httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier);
            var id = sidClaim.Value;
            var LoginName = nameIdentifierClaim.Value;
            var userDto = new SysUserDto() { Id = Guid.Parse(id), LoginName = LoginName };
            var menus = await GetMenusAndFunctionByUserAsync(userDto);
            return menus;
        }

        /// <summary>
        /// 仅获取当前票据用户菜单信息
        /// </summary>
        /// <param name="sysUserDto"></param>
        /// <returns></returns>
        public async Task<List<SysMenuDto>> GetMenusByCurrentUserAsync()
        {
            var menusAndFunctions = await this.GetMenusAndFunctionByCurrentUserAsync();
            menusAndFunctions = menusAndFunctions.Where(m => m.Type == 0).ToList();
            return menusAndFunctions;
        }

        /// <summary>
        /// 获取当前登录用户FunctionCode集合
        /// </summary>
        /// <param name="currUser"></param>
        /// <returns></returns>
        public async Task<List<string>> GetCurrUserFunctionCodeAsync(ClaimsPrincipal currUser)
        {
            var sidClaim = currUser.FindFirst(ClaimTypes.Sid);
            var nameIdentifierClaim = currUser.FindFirst(ClaimTypes.NameIdentifier);
            var id = sidClaim.Value;
            var LoginName = nameIdentifierClaim.Value;
            var userDto = new SysUserDto() { Id = Guid.Parse(id), LoginName = LoginName };
            var userFunctionList = await GetMenusAndFunctionByUserAsync(userDto);
            var serCodeList = from function in userFunctionList select function.Code;
            return serCodeList.ToList();
        }
    }
}
